/*
 * Qinlan Shen
 * November 8, 2010
 * Pd. 8
 * 
 * This JFrame holds on the major components of the game.  It also contains a GamePanel class which displays the Player, all Enemy
 * objects and Point items dropped by Rose.
 * 
 * Packet assignments:
 * 
 * 0 - red
 * 1 = blue
 * 2 - yellow
 * 3 - green
 * 4 - purple
 * 5 - orange
 * 6 - magenta
 * 7 - cyan
 * 8 - white
 * 9 - black
 * 
 */

import java.awt.event.*;
import java.awt.*;
import java.io.*;
import javax.imageio.*;
import javax.swing.*;
import java.util.*;

public class PerfectMathClass extends JFrame implements KeyListener
{
	Image charImg;
	Image roseImg;
	Image pointImg;
	
	Image redImg;
	Image blueImg;
	Image yellowImg;
	Image greenImg;
	Image purpleImg;
	Image orangeImg;
	Image magentaImg;
	Image cyanImg;
	Image whiteImg;
	Image blackImg;
	
	Image magicCircle;
	
	Image offscreen;
	JPanel infoPanel;
	JPanel scorePanel;
	JLabel score;
	JLabel scriptNum;
	JLabel graze;
	JLabel lives;
	Rose rose;
	GamePanel gp;
	Character player;
	ArrayList <PointItem> points;
	ArrayList <Image> packets;
	ArrayList <Enemy> enemies;
	ArrayList <RoseScript> scripts;
	boolean game;
	boolean focused;
	boolean up;
	boolean down;
	boolean left;
	boolean right;
	int difficulty;
	int time;
	int num;
	int frameKill;
	int packetsCleared;
	double multiplier;
	boolean[] cleared;
	boolean[] captured;
	
	//Sets up general screen
	public static void main(String[] args)
	{
		JFrame rpmc = new PerfectMathClass(9);
		rpmc.setSize(1024, 820);
		rpmc.setResizable(false);

	}
	public PerfectMathClass(int diff)
	{
		//Basic components of the screen
		super();
		setVisible(true);
		addKeyListener(this);
		Container c = getContentPane();
		c.setLayout(new BoxLayout(c, BoxLayout.X_AXIS));
		offscreen = createImage(640, 820);
		infoPanel = new JPanel();
		infoPanel.setLayout(new BoxLayout(infoPanel, BoxLayout.Y_AXIS));
		scorePanel = new JPanel(new GridLayout(5, 1));
		game = false;
		
		difficulty = diff;
		time = 15;
		num = 5;
		
		//ArrayLists of important objects
		scripts = new ArrayList<RoseScript>();
		readScripts(diff, time, num);
		cleared = new boolean[scripts.size()];
		captured = new boolean[scripts.size()];
		for (int i = 0; i < cleared.length; i++)
			cleared[i] = false;
		for (int i = 0; i < captured.length; i++)
			captured[i] = true;
		points = new ArrayList<PointItem>();
		enemies = new ArrayList<Enemy>();
		packets = new ArrayList<Image>();
		
		//Images are read in
		try
		{
			pointImg = ImageIO.read(new File("point.png"));
			blueImg = ImageIO.read(new File("blue.png"));
			redImg = ImageIO.read(new File("red.png"));
			yellowImg = ImageIO.read(new File("yellow.png"));
			greenImg = ImageIO.read(new File("green.png"));
			purpleImg = ImageIO.read(new File("purple.png"));
			orangeImg = ImageIO.read(new File("orange.png"));
			magentaImg = ImageIO.read(new File("magenta.png"));
			cyanImg = ImageIO.read(new File("cyan.png"));
			whiteImg = ImageIO.read(new File("white.png"));
			blackImg = ImageIO.read(new File("black.png"));
			charImg = ImageIO.read(new File("char.png"));
			roseImg = ImageIO.read(new File("rose.png"));
			
			magicCircle = ImageIO.read(new File("magiccirclefull.png"));
			
			packets.add(redImg);
			packets.add(blueImg);
			packets.add(yellowImg);
			packets.add(greenImg);
			packets.add(purpleImg);
			packets.add(orangeImg);
			packets.add(magentaImg);
			packets.add(cyanImg);
			packets.add(whiteImg);
			packets.add(blackImg);
			
			//Hitbox calculation for Rose and the main character
			int h = (charImg.getWidth(this) + charImg.getHeight(this))/4 * (charImg.getHeight(this)/charImg.getWidth(this));
			player = new Character(charImg, h, charImg.getWidth(this), charImg.getHeight(this));
			rose = new Rose(roseImg, 1, scripts.size(), roseImg.getWidth(this));
			
			enemies.add(rose);
			
			gp = new GamePanel();
			gp.setPreferredSize(new Dimension (640, 820));
			
			//Panel for score, graze and script number.
			scorePanel.setPreferredSize(new Dimension(384, 820));
			graze = new JLabel("Graze Points: " + player.getGraze());
			score = new JLabel("Score: " + player.getScore());
			scriptNum = new JLabel("Script Num: " + rose.getNumCurrentScript());
			lives = new JLabel("Extra Lives: " + player.getLives());
			scorePanel.add(score);
			scorePanel.add(scriptNum);
			scorePanel.add(new JLabel("Total Scripts: " + rose.getTotalScripts()));
			scorePanel.add(graze);
			scorePanel.add(lives);
			scorePanel.setBorder(BorderFactory.createTitledBorder("Score and Script"));
			infoPanel.add(scorePanel);
						
			c.add(gp);
			c.add(infoPanel);
			
			if (difficulty == 9)
				multiplier = .75;
			else if (difficulty == 10)
				multiplier = 1;
			else if (difficulty == 11)
				multiplier = 1.25;
			else
				multiplier = 1.5;
				

		}
		catch (Exception e)
		{
			System.out.println("File does not exist");
		}
	}
	
	public void readScripts(int d, int t, int n)
	{
		ArrayList<RoseScript> tempAllScripts = new ArrayList<RoseScript>();
		tempAllScripts.add(new MishagujiSama(d, t));
		tempAllScripts.add(new BorderWaveParticle(d, t));
		tempAllScripts.add(new MiracleFruit(d, t));
		tempAllScripts.add(new SakuNon(d, t));
		tempAllScripts.add(new BuretsuCrisis(d, t));

		if (n >= tempAllScripts.size())
			scripts = tempAllScripts;
		//Randomly determines which scripts to run
		else
		{
			for (int i = 0; i < n; i++)
			{
				scripts.add(tempAllScripts.remove((int)(Math.random() * tempAllScripts.size())));
			}
		}
	}
	
	public void keyPressed(KeyEvent e) 
	{
		if (!game)
		{
			game = true;
			gp.start();
		}
		if (e.getKeyCode() == e.VK_SHIFT)
			focused = true;
		else if (e.getKeyCode() == e.VK_LEFT)
			left = true;
		else if (e.getKeyCode() == e.VK_RIGHT)
			right = true;
		else if (e.getKeyCode() == e.VK_UP)
			up = true;
		else if (e.getKeyCode() == e.VK_DOWN)
			down = true;
	}
	
	public void keyReleased(KeyEvent e)
	{		
		if (e.getKeyCode() == e.VK_SHIFT)
			focused = false;
		else if (e.getKeyCode() == e.VK_LEFT)
			left = false;
		else if (e.getKeyCode() == e.VK_RIGHT)
			right = false;
		else if (e.getKeyCode() == e.VK_UP)
			up = false;
		else if (e.getKeyCode() == e.VK_DOWN)
			down = false;
	}
	
	public void keyTyped(KeyEvent e)
	{
		
	}
	
	//Panel for actual gameplay
	class GamePanel extends JPanel
	{
		MediaTracker tracker;
		AnimThread animator;
		Graphics2D doubleBuffer;
		Graphics2D magicRotate;
		
		public GamePanel()
		{
			tracker = new MediaTracker(this);
			doubleBuffer = (Graphics2D)(offscreen.getGraphics());
			tracker.addImage(charImg, 1);
			tracker.addImage(roseImg, 1);
			try
			{
				tracker.waitForAll();
			}
			catch(InterruptedException e)
			{
				
			}
		}
		
		public void start()
		{
			animator = new AnimThread();
			animator.start();
		}
		
		public void paintComponent(Graphics g)
		{
			super.paintComponent(g);
			update(g);
		}
		
		public void update(Graphics g)
		{	
			Graphics2D g2 = (Graphics2D)g;
			doubleBuffer.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON );
			doubleBuffer.setColor(Color.black);
			doubleBuffer.fillRect(0, 0, getWidth(), getHeight());
			if (!game)
			{
				doubleBuffer.setColor(Color.white);
				doubleBuffer.drawString("Dodge Mr. Rose's packets for a certain number of time using the arrow keys.", 100, 200);
				doubleBuffer.drawString("Press and hold the SHIFT key to focus, showing your hitbox (white dot on your ", 100, 220);
				doubleBuffer.drawString("back) and slowing you down.  Do not let Mr. Rose or any of his packets hit", 100, 240);
				doubleBuffer.drawString("your hitbox.  After the countdown timer in the top right hits 0, the current", 100, 260);
				doubleBuffer.drawString("pattern will end.  You will receive a bonus for clearing a pattern without", 100, 280);
				doubleBuffer.drawString("without dying.  Mr. Rose will also drop small blue point items which you can ", 100, 300);
				doubleBuffer.drawString("collect.  The higher the point items are on the screen when collected, the more", 100, 320);
				doubleBuffer.drawString("they are worth.  In addition to point items, 'grazing', or allowing packets to", 100, 340);
				doubleBuffer.drawString("your character's sprite without hitting the hitbox, will increase your score.", 100, 360);
				doubleBuffer.drawString("Survive a certain number of patterns to beat the game.  At the end of the game,", 100, 380);
				doubleBuffer.drawString("score will be calculated based on graze (*10), item, and pattern survival bonuses.", 100, 400);
				doubleBuffer.drawString("Difficulty Level: " + difficulty, 120, 440);
				doubleBuffer.drawString("Number of Scripts: " + num, 120, 460);
				doubleBuffer.drawString("Time per Script: " + time + " seconds", 120, 480);
				doubleBuffer.drawString("Number of Lives: " + (player.getLives() + 1), 120, 500);

				doubleBuffer.drawString("Press any key to continue", 250, 540);
			}
			else if (game && player.getActivity() && rose.getNumCurrentScript() <= rose.getTotalScripts())
			{	
				//Drawing the player
				if(!player.getInvincibility() || player.getInvincibility() && animator.getFrame() % 20 >= 0 && animator.getFrame() % 20 <= 10)
					doubleBuffer.drawImage(charImg, (int)Math.round(player.getX() - player.getImage().getWidth(this)/2), (int)Math.round(player.getY() - player.getImage().getHeight(this)/2), this);
				doubleBuffer.setColor(Color.GREEN);
				if(player.getInvincibility())
					doubleBuffer.drawOval((int)Math.round(player.getX()) - (180 - frameKill) / 2, (int)Math.round(player.getY())  - (180 - frameKill) / 2, 180 - frameKill, 180 - frameKill);
				doubleBuffer.setColor(Color.WHITE);
				if (focused)
					doubleBuffer.fillOval((int)Math.round(player.getX()) - 3, (int)Math.round(player.getY()) - 3, 6, 6);
				
				doubleBuffer.drawImage(magicCircle, (int)Math.round(rose.getX() - magicCircle.getWidth(this)/2), (int)Math.round(rose.getY() - magicCircle.getHeight(this)/2), this);
				
				//Draws enemies
				for (int j = 0; j < enemies.size(); j++)
				{
					try {
					if(j >= enemies.size()) 
						break;
					if (enemies.get(j) != null && j < enemies.size())
						doubleBuffer.drawImage(enemies.get(j).getImage(), (int)Math.round(enemies.get(j).getX() - enemies.get(j).getImage().getWidth(this)/2), (int)Math.round(enemies.get(j).getY() - enemies.get(j).getImage().getHeight(this)/2), this);
					} catch(Exception e) {continue; }
				}
				
				//Draws point items dropped by Mr. Rose
				for(int i = 0; i < points.size(); i++)
				{
					doubleBuffer.drawImage(points.get(i).getImage(), (int)Math.round(points.get(i).getX() - points.get(i).getImage().getWidth(this)/2), (int)(points.get(i).getY() - points.get(i).getImage().getHeight(this)/2), this);
				}
				if (rose.getNumCurrentScript() - 1 < scripts.size())
				{
					doubleBuffer.setColor(Color.white);
					doubleBuffer.drawString(scripts.get(rose.getNumCurrentScript() - 1).getTime() + "", 580, 50);
					doubleBuffer.setColor(Color.green);
					if(captured[rose.getNumCurrentScript()- 1])
						doubleBuffer.drawString("Bonus", 580, 70);
				}
			}
			
			else
			{
				doubleBuffer.setColor(Color.white);
				if (!player.getActivity())
					doubleBuffer.drawString("Thank you for playing.", 220, 300);
				else if(num > 1)
					doubleBuffer.drawString("Congratulations for surviving " + rose.getTotalScripts() + " scripts.", 220, 300);
				else
					doubleBuffer.drawString("Congratulations for surviving 1 script.", 220, 300);
				
				doubleBuffer.drawString("Graze Bonus: " + player.getGraze() * 10, 220, 340);
				doubleBuffer.drawString("Item Bonus: " + player.getItemBonuses(), 220, 360);
				doubleBuffer.drawString("Survival Bonus: " + packetsCleared * 50, 220, 380);

				doubleBuffer.drawString("Difficulty Multiplier: " + multiplier, 220, 400);
				doubleBuffer.drawString("Total Score: " + Math.round((player.getScore()) * multiplier), 220, 420);
				
				int numCaptured = 0;
				for (int i = 0; i < rose.getNumCurrentScript(); i++)
					if(i < rose.getTotalScripts() && captured[i])
						numCaptured++;
				doubleBuffer.drawString("Number of Scripts Captured: " + numCaptured , 220, 460);
				doubleBuffer.drawString("Total Number of Scripts Played: " + Math.min(rose.getNumCurrentScript(), rose.getTotalScripts()) , 220, 480);
			}
			g2.drawImage(offscreen, 0, 0, this);
		}			
	}
	
	class AnimThread extends Thread
	{
		int frame = 0;
		double actualSpeed;
		
		public int getFrame()
		{
			return frame;
		}
		
		public void run()
		{
			//Stuff that happens while the player is still active/Rose hasn't finished all scripts
			while (player.getActivity() && rose.getNumCurrentScript() <= scripts.size())
			{
				actualSpeed = player.getSpeed();
				if (focused)
					actualSpeed = player.getFocusSpeed();
				
				//Player control
				if(left)
				{
					if (up)
					{
						player.setXSpeed((int)(-actualSpeed/Math.sqrt(2)));
						player.setYSpeed((int)(-actualSpeed/Math.sqrt(2)));
					}
					else if (down)
					{
						player.setXSpeed((int)(-actualSpeed/Math.sqrt(2)));
						player.setYSpeed((int)(actualSpeed/Math.sqrt(2)));
					}
					else
					{
						player.setXSpeed(-actualSpeed);
						player.setYSpeed(0);
					}
				}
				else if(right)
				{
					if (up)
					{
						player.setXSpeed((int)(actualSpeed/Math.sqrt(2)));
						player.setYSpeed((int)(-actualSpeed/Math.sqrt(2)));
					}
					else if (down)
					{
						player.setXSpeed((int)(actualSpeed/Math.sqrt(2)));
						player.setYSpeed((int)(actualSpeed/Math.sqrt(2)));
					}
					else
					{
						player.setXSpeed(actualSpeed);
						player.setYSpeed(0);
					}
				}
				else if (up)
				{
					player.setXSpeed(0);
					player.setYSpeed(-actualSpeed);
				}
				else if (down)
				{
					player.setXSpeed(0);
					player.setYSpeed(actualSpeed);
				}
				else
				{
					player.setXSpeed(0);
					player.setYSpeed(0);
				}
				player.update();
				
				points = scripts.get(rose.getNumCurrentScript() - 1).run(rose, player, frame, enemies, points, packets, pointImg);
				
				if(rose.getNumCurrentScript() <= rose.getTotalScripts() && points.size() > 0 && scripts.get(rose.getNumCurrentScript() - 1).getTime() == time && frame != 0 && !cleared[rose.getNumCurrentScript() - 2])
				{
					if(captured[rose.getNumCurrentScript() - 2])
					{
						packetsCleared += enemies.size() - 1;
					}
					enemies = new ArrayList<Enemy>();
					enemies.add(rose);
					frame = -1;
					cleared[rose.getNumCurrentScript() - 2] = true;
	
				}

				
				//Updates enemies
				int i = 0;
				while (i < enemies.size())
				{
					enemies.get(i).update();
					
					//Checks if Mr. Rose kills the character
					if (enemies.get(i).playerHit(player) && !player.getInvincibility())
					{
						player.kill();
						System.out.println("killed");
						captured[rose.getNumCurrentScript() - 1] = false;
					}

					//Grazing
					else if (enemies.get(i).superPlayerHit(player) && frame % 15 == 0)
						player.incrementGraze();
					
					if (i > 0 && (enemies.get(i).getY() > 820 + enemies.get(i).getHitbox() || enemies.get(i).getX() + enemies.get(i).getHitbox() > 640 || enemies.get(i).getX() < -enemies.get(i).getHitbox() || enemies.get(i).getY() < -enemies.get(i).getHitbox()))
						enemies.remove(i);
					else
						i++;
				}
				

				if (player.getInvincibility())
					frameKill++;
				if (frameKill == 180)
				{
					player.setInvincibility(false);
					frameKill = 0;
				}
				
				i = 0;
				//Calculates positions of point items
				while (i < points.size())
				{
					points.get(i).update();
					if (points.get(i).playerHit(player))
					{
						player.addPointItem(points.get(i));
						points.remove(i);
					}
					else if (points.get(i).getY() > 820 + points.get(i).getHitbox())
						points.remove(i);
					else
						i++;
				}
				
				repaint();
				frame++;
				
				scriptNum.setText("Script Num: " + rose.getNumCurrentScript());
				graze.setText("Graze Points: " + player.getGraze());
				score.setText("Score: " + player.getScore());
				lives.setText("Extra Lives: " + player.getLives());
				scorePanel.repaint();
				
				try
				{
					Thread.sleep(17);
				}
				catch (InterruptedException e)
				{
				
				}
			}
			if(rose.getNumCurrentScript() >= rose.getTotalScripts() - 1 && captured[rose.getTotalScripts() - 1])
			{
				packetsCleared += enemies.size();
			}
			//Script bonuses for the final spell
			player.scriptBonuses(packetsCleared);
			scriptNum.setText("Script Num: " + Math.min(rose.getTotalScripts(), rose.getNumCurrentScript()));
			graze.setText("Graze Points: " + player.getGraze());
			score.setText("Score: " + Math.round((player.getScore()) * multiplier));
			lives.setText("Extra Lives: " + player.getLives());
			scorePanel.repaint();
			repaint();
		}		
	}
}